package com.example.suanfa;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class KMeansExample {

    public static void main(String[] args) {
        // 生成模拟数据
        List<List<Double>> temp = generateSimulatedData();

        int k = 3; // 簇的数量
        int maxIterations = 100;
//        double[][] centroids = kMeans(temp, k, maxIterations);

        // 计算数据的均值和方差
        double[][] mean = computeMean(temp);
        double[][] variance = computeVariance(temp);

        // 生成新数据点
        int numNewPoints = 100; // 生成 3 个新数据点
        List<List<Double>> newData = generateNewData(mean, variance, numNewPoints);

        // 预测新数据点的簇
//        int[] predictions = predictClusters(newData, centroids);

        // 打印生成的新数据点及其预测簇
        for (int i = 0; i < newData.size(); i++) {
            System.out.println(newData.get(i).toString());
        }

        List<List<Double>> newList = new ArrayList<>(temp);
        newList.addAll(newData);
    }

    public static double[][] kMeans(List<List<Double>> data, int k, int maxIterations) {
        int n = data.size();
        int d = data.get(0).size();
        double[][] centroids = new double[k][d];

        // 随机初始化质心
        Random random = new Random();
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < d; j++) {
                centroids[i][j] = data.get(random.nextInt(n)).get(j);
            }
        }

        // 迭代更新质心
        int[] assignments = new int[n];
        for (int iter = 0; iter < maxIterations; iter++) {
            // 分配数据点到最近的质心
            for (int i = 0; i < n; i++) {
                double minDist = Double.MAX_VALUE;
                int closestCentroid = 0;
                for (int j = 0; j < k; j++) {
                    double dist = euclideanDistance(data.get(i), centroids[j]);
                    if (dist < minDist) {
                        minDist = dist;
                        closestCentroid = j;
                    }
                }
                assignments[i] = closestCentroid;
            }

            // 更新质心
            double[][] newCentroids = new double[k][d];
            int[] counts = new int[k];
            for (int i = 0; i < n; i++) {
                int centroidIndex = assignments[i];
                for (int j = 0; j < d; j++) {
                    newCentroids[centroidIndex][j] += data.get(i).get(j);
                }
                counts[centroidIndex]++;
            }
            for (int i = 0; i < k; i++) {
                if (counts[i] > 0) {
                    for (int j = 0; j < d; j++) {
                        newCentroids[i][j] /= counts[i];
                    }
                }
            }
            centroids = newCentroids;
        }

        return centroids;
    }

    public static double euclideanDistance(List<Double> a, double[] b) {
        double sum = 0.0;
        for (int i = 0; i < a.size(); i++) {
            double diff = a.get(i) - b[i];
            sum += diff * diff;
        }
        return Math.sqrt(sum);
    }

    // 计算数据的均值
    public static double[][] computeMean(List<List<Double>> data) {
        int n = data.size();
        int d = data.get(0).size();
        double[][] mean = new double[d][1];

        for (int i = 0; i < d; i++) {
            double sum = 0.0;
            for (List<Double> point : data) {
                sum += point.get(i);
            }
            mean[i][0] = sum / n;
        }

        return mean;
    }

    // 计算数据的方差
    public static double[][] computeVariance(List<List<Double>> data) {
        int n = data.size();
        int d = data.get(0).size();
        double[][] mean = computeMean(data);
        double[][] variance = new double[d][1];

        for (int i = 0; i < d; i++) {
            double sum = 0.0;
            for (List<Double> point : data) {
                double diff = point.get(i) - mean[i][0];
                sum += diff * diff;
            }
            variance[i][0] = sum / (n - 1);
        }

        return variance;
    }

    // 生成新数据点
    public static List<List<Double>> generateNewData(double[][] mean, double[][] variance, int numPoints) {
        int d = mean.length;
        List<List<Double>> newData = new ArrayList<>();
        Random random = new Random();

        for (int i = 0; i < numPoints; i++) {
            List<Double> point = new ArrayList<>();
            for (int j = 0; j < d; j++) {
                point.add(random.nextGaussian() * Math.sqrt(variance[j][0]) + mean[j][0]);
            }
            newData.add(point);
        }

        return newData;
    }

    public static int[] predictClusters(List<List<Double>> newData, double[][] centroids) {
        int n = newData.size();
        int k = centroids.length;
        int[] assignments = new int[n];

        for (int i = 0; i < n; i++) {
            double minDist = Double.MAX_VALUE;
            int closestCentroid = 0;
            for (int j = 0; j < k; j++) {
                double dist = euclideanDistance(newData.get(i), centroids[j]);
                if (dist < minDist) {
                    minDist = dist;
                    closestCentroid = j;
                }
            }
            assignments[i] = closestCentroid;
        }

        return assignments;
    }

    private static List<List<Double>> generateSimulatedData() {
        List<List<Double>> temp = new ArrayList<>();
        Random random = new Random();
        int numDataPoints = 100; // 模拟数据点的数量
        int numDimensions = 2; // 数据的维度
        for (int i = 0; i < numDataPoints; i++) {
            List<Double> point = new ArrayList<>();
            for (int j = 0; j < numDimensions; j++) {
                point.add(random.nextDouble() * 10); // 生成 0 到 10 之间的随机数作为数据点的坐标
            }
            temp.add(point);
        }
        return temp;
    }
}
